<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>测试在textarea最后的单词后面有代码提示框</title>
  <style>
    .textarea,
    textarea {
      padding: 10px;
      font-family: inherit;
      width: 200px;
      height: 100px;
      resize: none;
      font-size: 14px;
      line-height: 1;
      border: 1px solid #ddd;
      white-space: pre-wrap;
      word-break: break-all;
    }
    .tip_box{
      width: 100px;
      height: 100px;
      border: solid 1px red;
      position: absolute;
      z-index: 1000;
      top: 0;
      right: 0;
      display: none;
      background: #fff;
      overflow: auto;
    }
  </style>
  <script src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
  <script >
    (function (root, factory) {
      if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(["jquery"], function ($) {
          return (root.returnExportsGlobal = factory($));
        });
      } else if (typeof exports === 'object') {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like enviroments that support module.exports,
        // like Node.
        module.exports = factory(require("jquery"));
      } else {
        factory(jQuery);
      }
    }(this, function ($) {

      /*
        Implement Github like autocomplete mentions
        http://ichord.github.com/At.js
        Copyright (c) 2013 chord.luo@gmail.com
        Licensed under the MIT license.
      */

      /*
      本插件操作 textarea 或者 input 内的插入符
      只实现了获得插入符在文本框中的位置，我设置
      插入符的位置.
      */

      "use strict";
      var EditableCaret, InputCaret, Mirror, Utils, discoveryIframeOf, methods, oDocument, oFrame, oWindow, pluginName, setContextBy;

      pluginName = 'caret';

      EditableCaret = (function() {
        function EditableCaret($inputor) {
          this.$inputor = $inputor;
          this.domInputor = this.$inputor[0];
        }

        EditableCaret.prototype.setPos = function(pos) {
          var fn, found, offset, sel;
          if (sel = oWindow.getSelection()) {
            offset = 0;
            found = false;
            (fn = function(pos, parent) {
              var node, range, _i, _len, _ref, _results;
              _ref = parent.childNodes;
              _results = [];
              for (_i = 0, _len = _ref.length; _i < _len; _i++) {
                node = _ref[_i];
                if (found) {
                  break;
                }
                if (node.nodeType === 3) {
                  if (offset + node.length >= pos) {
                    found = true;
                    range = oDocument.createRange();
                    range.setStart(node, pos - offset);
                    sel.removeAllRanges();
                    sel.addRange(range);
                    break;
                  } else {
                    _results.push(offset += node.length);
                  }
                } else {
                  _results.push(fn(pos, node));
                }
              }
              return _results;
            })(pos, this.domInputor);
          }
          return this.domInputor;
        };

        EditableCaret.prototype.getIEPosition = function() {
          return this.getPosition();
        };

        EditableCaret.prototype.getPosition = function() {
          var inputor_offset, offset;
          offset = this.getOffset();
          inputor_offset = this.$inputor.offset();
          offset.left -= inputor_offset.left;
          offset.top -= inputor_offset.top;
          return offset;
        };

        EditableCaret.prototype.getOldIEPos = function() {
          var preCaretTextRange, textRange;
          textRange = oDocument.selection.createRange();
          preCaretTextRange = oDocument.body.createTextRange();
          preCaretTextRange.moveToElementText(this.domInputor);
          preCaretTextRange.setEndPoint("EndToEnd", textRange);
          return preCaretTextRange.text.length;
        };

        EditableCaret.prototype.getPos = function() {
          var clonedRange, pos, range;
          if (range = this.range()) {
            clonedRange = range.cloneRange();
            clonedRange.selectNodeContents(this.domInputor);
            clonedRange.setEnd(range.endContainer, range.endOffset);
            pos = clonedRange.toString().length;
            clonedRange.detach();
            return pos;
          } else if (oDocument.selection) {
            return this.getOldIEPos();
          }
        };

        EditableCaret.prototype.getOldIEOffset = function() {
          var range, rect;
          range = oDocument.selection.createRange().duplicate();
          range.moveStart("character", -1);
          rect = range.getBoundingClientRect();
          return {
            height: rect.bottom - rect.top,
            left: rect.left,
            top: rect.top
          };
        };

        EditableCaret.prototype.getOffset = function(pos) {
          var clonedRange, offset, range, rect, shadowCaret;
          if (oWindow.getSelection && (range = this.range())) {
            if (range.endOffset - 1 > 0 && range.endContainer !== this.domInputor) {
              clonedRange = range.cloneRange();
              clonedRange.setStart(range.endContainer, range.endOffset - 1);
              clonedRange.setEnd(range.endContainer, range.endOffset);
              rect = clonedRange.getBoundingClientRect();
              offset = {
                height: rect.height,
                left: rect.left + rect.width,
                top: rect.top
              };
              clonedRange.detach();
            }
            if (!offset || (offset != null ? offset.height : void 0) === 0) {
              clonedRange = range.cloneRange();
              shadowCaret = $(oDocument.createTextNode("|"));
              clonedRange.insertNode(shadowCaret[0]);
              clonedRange.selectNode(shadowCaret[0]);
              rect = clonedRange.getBoundingClientRect();
              offset = {
                height: rect.height,
                left: rect.left,
                top: rect.top
              };
              shadowCaret.remove();
              clonedRange.detach();
            }
          } else if (oDocument.selection) {
            offset = this.getOldIEOffset();
          }
          if (offset) {
            offset.top += $(oWindow).scrollTop();
            offset.left += $(oWindow).scrollLeft();
          }
          return offset;
        };

        EditableCaret.prototype.range = function() {
          var sel;
          if (!oWindow.getSelection) {
            return;
          }
          sel = oWindow.getSelection();
          if (sel.rangeCount > 0) {
            return sel.getRangeAt(0);
          } else {
            return null;
          }
        };

        return EditableCaret;

      })();

      InputCaret = (function() {
        function InputCaret($inputor) {
          this.$inputor = $inputor;
          this.domInputor = this.$inputor[0];
        }

        InputCaret.prototype.getIEPos = function() {
          var endRange, inputor, len, normalizedValue, pos, range, textInputRange;
          inputor = this.domInputor;
          range = oDocument.selection.createRange();
          pos = 0;
          if (range && range.parentElement() === inputor) {
            normalizedValue = inputor.value.replace(/\r\n/g, "\n");
            len = normalizedValue.length;
            textInputRange = inputor.createTextRange();
            textInputRange.moveToBookmark(range.getBookmark());
            endRange = inputor.createTextRange();
            endRange.collapse(false);
            if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
              pos = len;
            } else {
              pos = -textInputRange.moveStart("character", -len);
            }
          }
          return pos;
        };

        InputCaret.prototype.getPos = function() {
          if (oDocument.selection) {
            return this.getIEPos();
          } else {
            return this.domInputor.selectionStart;
          }
        };

        InputCaret.prototype.setPos = function(pos) {
          var inputor, range;
          inputor = this.domInputor;
          if (oDocument.selection) {
            range = inputor.createTextRange();
            range.move("character", pos);
            range.select();
          } else if (inputor.setSelectionRange) {
            inputor.setSelectionRange(pos, pos);
          }
          return inputor;
        };

        InputCaret.prototype.getIEOffset = function(pos) {
          var h, textRange, x, y;
          textRange = this.domInputor.createTextRange();
          pos || (pos = this.getPos());
          textRange.move('character', pos);
          x = textRange.boundingLeft;
          y = textRange.boundingTop;
          h = textRange.boundingHeight;
          return {
            left: x,
            top: y,
            height: h
          };
        };

        InputCaret.prototype.getOffset = function(pos) {
          var $inputor, offset, position;
          $inputor = this.$inputor;
          if (oDocument.selection) {
            offset = this.getIEOffset(pos);
            offset.top += $(oWindow).scrollTop() + $inputor.scrollTop();
            offset.left += $(oWindow).scrollLeft() + $inputor.scrollLeft();
            return offset;
          } else {
            offset = $inputor.offset();
            position = this.getPosition(pos);
            return offset = {
              left: offset.left + position.left - $inputor.scrollLeft(),
              top: offset.top + position.top - $inputor.scrollTop(),
              height: position.height
            };
          }
        };

        InputCaret.prototype.getPosition = function(pos) {
          var $inputor, at_rect, end_range, format, html, mirror, start_range;
          $inputor = this.$inputor;
          format = function(value) {
            value = value.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g, "<br/>");
            if (/firefox/i.test(navigator.userAgent)) {
              value = value.replace(/\s/g, '&nbsp;');
            }
            return value;
          };
          if (pos === void 0) {
            pos = this.getPos();
          }
          start_range = $inputor.val().slice(0, pos);
          end_range = $inputor.val().slice(pos);
          html = "<span style='position: relative; display: inline;'>" + format(start_range) + "</span>";
          html += "<span id='caret' style='position: relative; display: inline;'>|</span>";
          html += "<span style='position: relative; display: inline;'>" + format(end_range) + "</span>";
          mirror = new Mirror($inputor);
          return at_rect = mirror.create(html).rect();
        };

        InputCaret.prototype.getIEPosition = function(pos) {
          var h, inputorOffset, offset, x, y;
          offset = this.getIEOffset(pos);
          inputorOffset = this.$inputor.offset();
          x = offset.left - inputorOffset.left;
          y = offset.top - inputorOffset.top;
          h = offset.height;
          return {
            left: x,
            top: y,
            height: h
          };
        };

        return InputCaret;

      })();

      Mirror = (function() {
        Mirror.prototype.css_attr = ["borderBottomWidth", "borderLeftWidth", "borderRightWidth", "borderTopStyle", "borderRightStyle", "borderBottomStyle", "borderLeftStyle", "borderTopWidth", "boxSizing", "fontFamily", "fontSize", "fontWeight", "height", "letterSpacing", "lineHeight", "marginBottom", "marginLeft", "marginRight", "marginTop", "outlineWidth", "overflow", "overflowX", "overflowY", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "textAlign", "textOverflow", "textTransform", "whiteSpace", "wordBreak", "wordWrap"];

        function Mirror($inputor) {
          this.$inputor = $inputor;
        }

        Mirror.prototype.mirrorCss = function() {
          var css,
                  _this = this;
          css = {
            position: 'absolute',
            left: -9999,
            top: 0,
            zIndex: -20000
          };
          if (this.$inputor.prop('tagName') === 'TEXTAREA') {
            this.css_attr.push('width');
          }
          $.each(this.css_attr, function(i, p) {
            return css[p] = _this.$inputor.css(p);
          });
          return css;
        };

        Mirror.prototype.create = function(html) {
          this.$mirror = $('<div></div>');
          this.$mirror.css(this.mirrorCss());
          this.$mirror.html(html);
          this.$inputor.after(this.$mirror);
          return this;
        };

        Mirror.prototype.rect = function() {
          var $flag, pos, rect;
          $flag = this.$mirror.find("#caret");
          pos = $flag.position();
          rect = {
            left: pos.left,
            top: pos.top,
            height: $flag.height()
          };
          this.$mirror.remove();
          return rect;
        };

        return Mirror;

      })();

      Utils = {
        contentEditable: function($inputor) {
          return !!($inputor[0].contentEditable && $inputor[0].contentEditable === 'true');
        }
      };

      methods = {
        pos: function(pos) {
          if (pos || pos === 0) {
            return this.setPos(pos);
          } else {
            return this.getPos();
          }
        },
        position: function(pos) {
          if (oDocument.selection) {
            return this.getIEPosition(pos);
          } else {
            return this.getPosition(pos);
          }
        },
        offset: function(pos) {
          var offset;
          offset = this.getOffset(pos);
          return offset;
        }
      };

      oDocument = null;

      oWindow = null;

      oFrame = null;

      setContextBy = function(settings) {
        var iframe;
        if (iframe = settings != null ? settings.iframe : void 0) {
          oFrame = iframe;
          oWindow = iframe.contentWindow;
          return oDocument = iframe.contentDocument || oWindow.document;
        } else {
          oFrame = void 0;
          oWindow = window;
          return oDocument = document;
        }
      };

      discoveryIframeOf = function($dom) {
        var error;
        oDocument = $dom[0].ownerDocument;
        oWindow = oDocument.defaultView || oDocument.parentWindow;
        try {
          return oFrame = oWindow.frameElement;
        } catch (_error) {
          error = _error;
        }
      };

      $.fn.caret = function(method, value, settings) {
        var caret;
        if (methods[method]) {
          if ($.isPlainObject(value)) {
            setContextBy(value);
            value = void 0;
          } else {
            setContextBy(settings);
          }
          caret = Utils.contentEditable(this) ? new EditableCaret(this) : new InputCaret(this);
          return methods[method].apply(caret, [value]);
        } else {
          return $.error("Method " + method + " does not exist on jQuery.caret");
        }
      };

      $.fn.caret.EditableCaret = EditableCaret;

      $.fn.caret.InputCaret = InputCaret;

      $.fn.caret.Utils = Utils;

      $.fn.caret.apis = methods;


    }));
  </script>
</head>

<body>
<h3>这是一个测试</h3>
<div style="position:relative">
  <textarea id="code" style="background-color:black;color: white; width: 300px;height: 200px;" ></textarea>
  <div class="tip_box"></div>
</div>
<br>
</body>
<script>

  var table_list=['t_aa','t_bb','t_cc','xxoo','xyz']

  //判断字符是否为空的方法
  function isNotEmpty(obj){
    if(typeof obj == "undefined" || obj == null || obj == ""){
      return false;
    }else{
      return true;
    }
  }

  // 获取光标位置
  var rangeData = {start: 0, end: 0, text: "" };

  var $input = $('#code');

  function getCursortPosition (ctrl)
  {
    //获取光标位置函数
    var CaretPos = 0;
    CaretPos =ctrl.selectionStart; // 获取选定区的开始点
    return CaretPos;
  }
  // 以下是jQuery语法
  $(function(){
    function resetOffset($textField) {
      // console.log('offset', offset)
      // console.log('position', position)

      var offset = $textField.caret('offset');
      var position = $textField.caret('position');
      $('.tip_box').css('display', 'block')
      $('.tip_box').css('left', position.left)
      $('.tip_box').css('top', (position.top+20))
      return offset
    }

    // 匹配到了之后需要隐藏窗口
    function hideTip(){
      $('.tip_box').css('display', 'none')
    }

    var text_data = $("#code").val()
    //  松开键盘时候的操作
    $("#code").keyup(function(e){
      const { keyCode } = e
      // 空格时候隐藏tips下拉窗口
      if(keyCode === 32){
        hideTip();
      }

      all_data = $("#code").val().toString();
      // 获取最后一个单词
      last_word = all_data.split(" ").splice(-1);
      //  console.log(last_word);


      // 定义新的数组用来存放新的数据内容
      var new_table_list=[];
      $(".tip_box").empty();    // 匹配之前先清空内容
      for (var i=0;i<table_list.length;i++){
        // 如果能匹配到的话就显示出来
        num = table_list[i].indexOf(last_word)
        if ( num == 0 && isNotEmpty(last_word)){    // 并且最后的字符串不为空
          $(".tip_box").append(table_list[i]+"<br>");
          resetOffset($input);
        }
      }
    })

  })
</script>


</html>
